#include "mmap.h"

static int sma_initialized = 0; /* true if the sma has been initialized */
static int sma_numseg;          /* number of shm segments to allow */
static int sma_segsize;         /* size of each shm segment */
static int *sma_segments;       /* array of shm segment ids */
static void **sma_shmaddrs;     /* array of shm segment addresses */
static int sma_lastseg = 0;     /* index of MRU segment */
static int sma_lock;            /* sempahore to serialize access */

void *apc_sma_malloc(size_t n);

/**
 * 创建可用空间
 * 
 * @description: 来自APC源码
 * @Date: 2021-06-29 16:37:40
 * @param {int} size
 * @return {*}
 */
void *create_mmap(int size)
{
    void *shmaddr;
    shmaddr = (void *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
    if (shmaddr == MAP_FAILED)
    {
        php_printf("创建内存空间出错");
        exit(0);
    }
    return shmaddr;
}

/**
 * 释放可用空间
 * 
 * @description: 来自APC源码
 * @Date: 2021-06-29 16:37:53
 * @param {void} *shmaddr
 * @param {int} size
 * @return {*}
 */
void memory_unmap(void *shmaddr, int size)
{
    munmap(shmaddr, size);
}

/**
 * 动态地分配内存空间
 * 
 * @description: 来自APC源码
 * @Date: 2021-06-29 16:38:03
 * @param {size_t} n
 * @return {*}
 */
void *apc_emalloc(size_t n)
{
    void *p = (void *)malloc(n);
    if (p == NULL)
    {
        php_printf("apc_emalloc: malloc failed to allocate %u bytes:", n);
    }
    return p;
}

/**
 * 对齐字到系统边界
 * 
 * @description: 来自APC源码
 * @Date: 2021-06-29 16:38:33
 * @param {int} x
 * @return {*}
 */
static int alignword(int x)
{
    typedef union
    {
        void *p;
        int i;
        long l;
        double d;
        void (*f)();
    } word_t;
    return sizeof(word_t) * (1 + ((x - 1) / sizeof(word_t)));
}

/**
 * 初始化MMAP空间
 * 
 * @description: 来自APC源码
 * @Date: 2021-06-29 16:38:47
 * @param {*}
 * @return {*}
 */
void apc_sma_init()
{
    int i;

    sma_numseg = 1;
    sma_segsize = DEFAULT_SEGSIZE;

    sma_segments = (int *)apc_emalloc(sma_numseg * sizeof(int));
    sma_shmaddrs = (void **)apc_emalloc(sma_numseg * sizeof(void *));

    for (i = 0; i < sma_numseg; i++)
    {
        header_t *header;
        block_t *block;
        void *shmaddr;
        sma_segments[i] = sma_segsize;
        sma_shmaddrs[i] = create_mmap(sma_segsize);
        shmaddr = sma_shmaddrs[i];

        header = (header_t *)shmaddr;
        header->segsize = sma_segsize;
        header->avail = sma_segsize - sizeof(header_t) - sizeof(block_t) -
                        alignword(sizeof(int));

        block = BLOCKAT(sizeof(header_t)); //空间首地址+header_t大小 是block的首地址
        block->size = 0;
        block->next = sizeof(header_t) + sizeof(block_t); //下一块的大小还是header_t
        //为了方便获取内存时的遍历 判断是否为0
        block = BLOCKAT(block->next);
        block->size = header->avail;
        block->next = 0;
    }
}

/**
 * 获取可用空间
 * 
 * @description: 来自APC源码
 * @Date: 2021-06-29 16:39:11
 * @param {void} *shmaddr
 * @param {int} size
 * @return {*}
 */
int sma_allocate(void *shmaddr, int size)
{
    header_t *header;    /* header of shared memory segment */
    block_t *prv;        /* block prior to working block */
    block_t *cur;        /* working block in list */
    block_t *prvbestfit; /* block before best fit */
    int realsize;        /* actual size of block needed, including header */
    int minsize;         /* for finding best fit */

    /* Realsize must be aligned to a word boundary on some architectures. */
    realsize = alignword(max(size + alignword(sizeof(int)), sizeof(block_t)));

    /*
     * Set realsize to the smallest power of 2 greater than or equal to
     * realsize. This increases the likelihood that neighboring blocks can be
     * coalesced, reducing memory fragmentation.
     */
    if (POWER_OF_TWO_BLOCKSIZE)
    {
        int p = 1;

        while (p < realsize)
        {
            p <<= 1;
        }
        realsize = p;
    }

    /*
     * First, insure that the segment contains at least realsize free bytes,
     * even if they are not contiguous.
     */
    header = (header_t *)shmaddr;
    if (header->avail < realsize)
    {
        return -1;
    }

    prvbestfit = 0;    /* initially null (no fit) */
    minsize = INT_MAX; /* used to find best fit */

    prv = BLOCKAT(sizeof(header_t));
    while (prv->next != 0)
    {
        cur = BLOCKAT(prv->next);
        if (cur->size == realsize)
        {
            /* found a perfect fit, stop searching */
            prvbestfit = prv;
            break;
        }
        else if (cur->size > (sizeof(block_t) + realsize) &&
                 cur->size < minsize)
        {
            /* cur is acceptable and is the smallest so far */
            prvbestfit = prv;
            minsize = cur->size;
        }
        prv = cur;
    }

    if (prvbestfit == 0)
    {
        return -1;
    }

    prv = prvbestfit;
    cur = BLOCKAT(prv->next);

    /* update the block header */
    header->avail -= realsize;

    if (cur->size == realsize)
    {
        /* cur is a perfect fit for realsize; just unlink it */
        prv->next = cur->next;
    }
    else
    {
        block_t *nxt;  /* the new block (chopped part of cur) */
        int nxtoffset; /* offset of the block currently after cur */
        int oldsize;   /* size of cur before split */

        /* bestfit is too big; split it into two smaller blocks */
        nxtoffset = cur->next;
        oldsize = cur->size;
        prv->next += realsize;
        cur->size = realsize;
        nxt = BLOCKAT(prv->next);
        nxt->next = nxtoffset;
        nxt->size = oldsize - realsize;
    }

    return OFFSET(cur) + alignword(sizeof(int));
}

/**
 * 获取可用空间
 * 
 * @description: 来自APC源码
 * @Date: 2021-06-29 16:39:26
 * @param {size_t} n
 * @return {*}
 */
void *apc_sma_malloc(size_t n)
{
    int off;
    int i;

    off = sma_allocate(sma_shmaddrs[sma_lastseg], n);
    if (off != -1)
    {
        void *p = sma_shmaddrs[sma_lastseg] + off;
        return p;
    }

    for (i = 0; i < sma_numseg; i++)
    {
        if (i == sma_lastseg)
        {
            continue;
        }
        off = sma_allocate(sma_shmaddrs[i], n);
        if (off != -1)
        {
            void *p = sma_shmaddrs[i] + off;
            sma_lastseg = i;
            return p;
        }
    }

    return NULL;
}

static int sma_deallocate(void *shmaddr, int offset)
{
    header_t *header; /* header of shared memory segment */
    block_t *cur;     /* the new block to insert */
    block_t *prv;     /* the block before cur */
    block_t *nxt;     /* the block after cur */
    int size;         /* size of deallocated block */

    offset -= alignword(sizeof(int));
    assert(offset >= 0);

    /* find position of new block in free list */
    prv = BLOCKAT(sizeof(header_t));
    while (prv->next != 0 && prv->next < offset)
    {
        prv = BLOCKAT(prv->next);
    }

    /* insert new block after prv */
    cur = BLOCKAT(offset);
    cur->next = prv->next;
    prv->next = offset;

    /* update the block header */
    header = (header_t *)shmaddr;
    header->avail += cur->size;
    size = cur->size;

    if (((char *)prv) + prv->size == (char *)cur)
    {
        /* cur and prv share an edge, combine them */
        prv->size += cur->size;
        prv->next = cur->next;
        cur = prv;
    }

    nxt = BLOCKAT(cur->next);
    if (((char *)cur) + cur->size == (char *)nxt)
    {
        /* cur and nxt shared an edge, combine them */
        cur->size += nxt->size;
        cur->next = nxt->next;
    }

    return size;
}

void apc_sma_free(void *p)
{
    int i;

    if (p == NULL)
    {
        return;
    }

    for (i = 0; i < sma_numseg; i++)
    {
        if (p >= sma_shmaddrs[i] && (p - sma_shmaddrs[i]) < sma_segsize)
        {
            sma_deallocate(sma_shmaddrs[i], p - sma_shmaddrs[i]);
            return;
        }
    }

    php_printf("apc_sma_free: could not locate address %p", p);
}

/**
 * 创建Cache
 * 
 * @description: 来自APC源码
 * @Date: 2021-06-29 16:39:37
 * @param {int} size_hint
 * @param {int} gc_ttl
 * @return {*}
 */
apc_cache_t *apc_cache_create(int size_hint, int gc_ttl)
{
    apc_cache_t *cache;
    int cache_size;
    int num_slots;
    int i;
    num_slots = size_hint > 0 ? size_hint * 2 : 1000;

    cache = (apc_cache_t *)apc_emalloc(sizeof(apc_cache_t));
    cache_size = sizeof(cache_header_t) + num_slots * sizeof(slot_t *);
    cache->shmaddr = apc_sma_malloc(cache_size);
    memset(cache->shmaddr, 0, cache_size);

    cache->header = (cache_header_t *)cache->shmaddr; //新建一个header头 这个头不是空间的头了而是cache的头所以结构不一样
    cache->header->num_hits = 0;
    cache->header->num_misses = 0;
    cache->header->deleted_list = NULL;

    cache->slots = (slot_t **)(((char *)cache->shmaddr) + sizeof(cache_header_t)); //可用地址加上一个cache的头
    cache->num_slots = num_slots;
    cache->gc_ttl = gc_ttl;
    cache->lock = 0;

    for (i = 0; i < num_slots; i++)
    {
        cache->slots[i] = NULL; //设置每个slots的空间为空
    }

    return cache;
}

/**
 * 计算Key值（弃用）
 * 
 * @description: 来自APC源码
 * @Date: 2021-06-29 16:39:46
 * @param {char} *key
 * @return {*}
 */
static int hash(char *key)
{
    //做了修改，弃用
    return atoi(key);
    // return key.device + key.inode;
}
#define key_equals(a, b) (a.inode == b.inode && a.device == b.device)

/**
 * 组建插槽
 * 
 * @description: 来自APC源码
 * @Date: 2021-06-29 16:40:04
 * @param {int} key
 * @param {ulong} value
 * @param {slot_t} *next
 * @return {*}
 */
slot_t *make_slot(int key, long value, slot_t *next)
{
    slot_t *p = apc_sma_malloc(sizeof(slot_t));
    if (p == NULL)
        return NULL;
    p->key = key;
    p->expire_time = value;
    p->create_time = mtime();
    p->next = next;
    return p;
}

/**
 * 释放插槽
 * 
 * @description: iwonmo
 * @Date: 2021-06-29 17:18:39
 * @param {slot_t} *slot
 * @return {*}
 */
void free_slot(slot_t *slot)
{
    apc_sma_free(slot);
}

/**
 * 移除Key
 * 
 * @description: iwonmo
 * @Date: 2021-06-29 17:01:09
 * @param {apc_cache_t} *cache
 * @param {int} key
 * @return {int}
 */
int slot_key_remove(apc_cache_t *cache, int key)
{
    slot_t *slot;
    ulong now;

    now = mtime();

    if (cache->slots[key] != NULL)
    {
        slot = cache->slots[key];
        int gc_sec = now - slot->create_time;

        if (gc_sec > slot->expire_time)
        {
            slot_t *dead = slot;
            free_slot(dead);
            cache->slots[key] = NULL; /* 必须要置空，不然 apc_slot_expire_time 还会认为它是真 */
            return 1;
        }
    }
    return 0;
}

/**
 * 
 * 
 * @description: 来自APC源码
 * @Date: 2021-06-29 16:40:16
 * @param {*}
 * @return {*}
 */
int apc_cache_insert(apc_cache_t *cache, int key, int value)
{
    slot_t **slot;
    if (!value)
    {
        return 0;
    }

    /* 看是否需要删除这个KEY */
    slot_key_remove(cache, key);

    /* 获取过期时间 */
    int expire_time = apc_slot_expire_time(cache, key);

    /* 如果存在过期时间则不会继续插入 */
    if (expire_time > 0)
    {
        return expire_time;
    }

    /* 获取这个插槽的地址空间 */
    slot = &cache->slots[key];

    /* 插槽赋值 */
    if ((*slot = make_slot(key, value, *slot)) == NULL)
    {
        return 0;
    }

    return 1;
}

typedef void *(*apc_malloc_t)(size_t);
void *apc_xmemcpy(const void *p, size_t n, apc_malloc_t f)
{
    void *q;

    if (p != NULL && (q = f(n)) != NULL)
    {
        memcpy(q, p, n);
        return q;
    }
    return NULL;
}

void *apc_xstrdup(const char *s, apc_malloc_t f)
{
    return s != NULL ? apc_xmemcpy(s, strlen(s) + 1, f) : NULL;
}

/**
 * 将指针字符串转移到MMAP空间
 * 
 * @description: 来自APC源码
 * @Date: 2021-06-29 16:40:34
 * @param {const char} *str
 * @return {*}
 */
char *apc_cache_make_str(const char *str)
{
    char *strs;
    strs = apc_xstrdup(str, apc_sma_malloc); //注意这里是直接传参的内存获取函数
    if (!strs)
        return NULL;
    return strs;
}

/**
 * 获取插槽对象
 * 
 * @description: iwonmo
 * @Date: 2021-06-29 17:32:01
 * @param {apc_cache_t} *cache
 * @param {int} key
 * @return {*}
 */
void *apc_slot_find(apc_cache_t *cache, int key)
{
    slot_t **slot;
    slot = &cache->slots[key];
    return *slot;
}

/**
 * 获取Key的过期时间
 * 
 * @description: 来自APC源码
 * @Date: 2021-06-29 16:40:53
 * @param {apc_cache_t} *cache
 * @param {int} key
 * @return {*}
 */
int apc_slot_expire_time(apc_cache_t *cache, int key)
{
    slot_t *slot = cache->slots[key];
    if (slot != NULL)
    {
        return slot->expire_time;
    }
    return 0;
}

/**
 * 获取毫秒
 * 
 * @description: iwonmo
 * @Date: 2021-06-29 16:41:07
 * @param {*}
 * @return {*}
 */
ulong mtime()
{
    struct timeval t;
    gettimeofday(&t, NULL);
    return ((ulong)t.tv_sec) * 1000 + (ulong)t.tv_usec / 1000;
}

/**
 * 初始化Module
 * 
 * @description: 来自APC源码
 * @Date: 2021-06-29 16:43:32
 * @param {*}
 * @return {*}
 */
int apc_module_init()
{
    //初始化内存结构
    apc_sma_init();
    return 0;
}

void apc_sma_cleanup()
{
    int i;

    for (i = 0; i < sma_numseg; i++)
    {
        munmap(sma_shmaddrs[i], sma_segments[i]);
    }
}

/**
 * 打印所有的slot（插槽）
 * 
 * @description: iwonmo
 * @Date: 2021-06-29 16:37:04
 * @param {apc_cache_t} *cache
 * @return {*}
 */
void apc_debug_slot_list(apc_cache_t *cache)
{
    slot_t *slot;
    int i;

    for (i = 0; i < 2000; i++)
    {
        if (cache->slots[i] != NULL)
        {
            slot = cache->slots[i];
            php_printf("\nslot—index：%d，slot-expire-time：%ld，slot-create-time：%ld\n", slot->key, slot->expire_time, slot->create_time);
        }
    }
}